nm
查看 目标文件(.o、.a、.so、可执行文件)中的符号表,也就是列出程序中有哪些函数、变量、符号,以及它们在文件中的地址和类型。
nm [option(s)] [file(s)]
选项:
- -C 还原C++函数名(demangle),否则C++函数显示为编译后的符号名(mangled)
- -D 打印动态符号
- -n 按地址排序符号
- -u 只显示未定义(undefined)的符号(外部依赖)
- --defined-only 只列出已定义的符号
- -l 使用对象文件中的调试信息打印出所在源文件及行号;
| 字母 | 含义 |
T | 已定义的代码(text段) |
U | 未定义符号(external依赖) |
D | 已定义的数据(data段) |
B | BSS段(未初始化数据) |
R | 只读数据(readonly data) |
W | 弱符号(weak symbol) |
t/d/b | 表示局部符号(local,非大写) |
注意:
-C 总是适用于c++编译出来的对象文件。还记得c++中有重载么?为了区分重载函数,c++编译器会将函数返回值/参数等信息附加到函数名称中去形成一个mangle过的符号,那用这个选项列出符号的时候,做一个逆操作,输出那些原始的、我们可理解的符号名称。
使用 -l 时,必须保证你的对象文件中带有符号调式信息,这一般要求你在编译的时候指定一个 -g 选项。
使用nm前,最好先用 file 查看对象文件所属处理器架构,然后再用相应交叉版本的nm工具。
ldd
查看一个可执行文件在运行时依赖哪些共享库(动态链接库 .so),以及这些库从哪里加载。
ldd显示可执行模块的dependency的工作原理,其实质是通过ld-linux.so(elf动态库的装载器)来实现的,ld-linux.so模块会先于executable模块程序工作,并获得控制权,因此当上述的那些环境变量被设置时,ld-linux.so选择了显示可执行模块的dependency。
/lib/ld-linux.so.2 --list program(这相当于ldd program)
| 工具 | 功能 |
ldd | 查看运行时依赖的共享库路径 |
readelf -d | 查看ELF文件中 .dynamic 段(静态声明) |
objdump -p | 也可以查看动态依赖信息 |
objdump
objdump命令是Linux下的反汇编目标文件或者可执行文件的命令
选项
-a
显示档案库的成员信息,类似ls -l将lib*.a的信息列出
-b bfdname
指定目标码格式。这不是必须的,objdump能自动识别许多格式
-g
显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来
-C 将底层的符号名解码成用户级名字,除了去掉所开头的下划线之外,还使得C++函数名以可理解的方式显示出来。
-e
类似-g选项,但是生成的信息是和ctags工具相兼容的格式。
-d
从objfile中反汇编那些特定指令机器码的section。
-D
与 -d 类似,但反汇编所有section.
-EB
-EL 指定目标文件的小端。这个项将影响反汇编出来的指令。在反汇编的文件没描述小端信息的时候用。例如S-records.
-f
--file-headers
显示objfile中每个文件的整体头部摘要信息。
-h
显示目标文件各个section的头部摘要信息
-j name
仅仅显示指定名称为name的section的信息
-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
-R
显示文件的动态重定位入口,仅仅对于动态目标文件意义,比如某些共享库。
-s
显示指定section的完整内容。默认所有的非空section都会被显示。
-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
-t
显示文件的符号表入口。类似于nm -s提供的信息
-T 显示文件的动态符号表入口,仅仅对动态目标文件意义,比如某些共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息
-x
显示所可用的头信息,包括符号表、重定位入口。-x 等价于-a -f -h -r -t 同时指定。
-z
一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。
- View all data/code in every section of an ELF file:
objdump -D <elf_object> - View only program code in an ELF file:
objdump -d <elf_object> - View all symbols:
objdump -tT <elf_object>
objcopy
objcopy -O bfdname infile outfile
objcopy用于将object的部分获全部内容拷贝到另一个object,从而可以实现格式的变换。
raw_binary文件是没有任何结构的,其内容就是赤裸裸的代码与数据的组合体。
将xxx.o转换成s-record文件中,通常涉及到text段
选项
-S
去掉源文件的符号信息和relocation信息
-g
去除掉调试符号信息和相关的段
-R sectionname
去除掉由sectionname指定的section
-O bfdname
输出文件的bfdname
-K symbolname
保留由symbolname指定的符号信息
-x
处理符号
-N symbolname
去除掉由symbolname指定的符号信息
ld
-T File --script FILE指定一个链接器脚本文件,指示编译器按照脚本进行链接;
-Tbss ADDRESS bss段的链接地址;
-Ttext ADDRESS 代码段链接地址;
-Tdata ADDRESS 数据段链接地址;
-r --relocateable 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld'的输入,这经常被叫做“部分链接”,当我们需要将几个小的.o文件链接成为一个.o文件的时候,需要使用此选项。
-e 指定程序的入口标号;
-l LibName 指定要链接的库;
-L Directory 增加库文件的搜索路径;
-o FILE 设置输出文件名;
-O 输出文件最优;
-a 指定arch体系; ENTRY(_start) /* 设置程序入口符号 */
MEMORY { /* 可选:用于嵌入式设置内存区域 */
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 64K
}
SECTIONS { /* 核心段定义 */
. = 0x80000000; /* 指定链接起始地址 */
.text : {
*(.init) *(.text) *(.rodata) *(.text.*)
}
.data : {
*(.data) *(.data.*)
}
.bss : {
*(.bss) *(COMMON)
}
} _start | 通常为程序入口 |
_edata | 数据段结束地址 |
_etext | 代码段结束地址 |
_end | 所有段结束地址 |
. | 当前链接地址指针 |
ar
创建或更新 .a 静态库(最常用)
ar rcs libfoo.a foo.o bar.o 说明:
-
r:替换或新增成员 -
c:如果库不存在则创建 -
s:更新符号表(建议用于所有静态库)
查看库中有哪些对象文件
ar t libfoo.a 加 v 显示详细:
ar tv libfoo.a 解压库中的某个或所有 .o 文件
ar x libfoo.a # 解压所有
ar x libfoo.a bar.o # 解压指定成员 删除库中的某个成员
ar d libfoo.a old.o 追加多个目标文件到已有 .a 文件中
ar rcs libfoo.a new1.o new2.o 注意事项
-
.a文件是纯二进制归档,不包含依赖信息,使用时需显式链接,例如:gcc main.o -L. -lfoo -o main - 编译时建议使用
ranlib生成符号索引(但ar rcs已包含效果):ranlib libfoo.a
示例:制作一个静态库
- 编译源文件为目标文件:
gcc -c foo.c bar.c - 创建静态库:
ar rcs libfoo.a foo.o bar.o - 链接使用:
gcc main.c -L. -lfoo -o main
readelf
查看 ELF 文件类型和基本信息
readelf -h ./a.out 输出内容包括:
- 类型(可执行文件/共享库/可重定位)
- 架构(x86_64, riscv, arm 等)
- 入口地址
- 节区数、程序段数
查看程序加载段信息(运行时映射)
readelf -l ./a.out 显示 .text, .data, .bss 等段的加载地址、权限(R/W/X)等信息。
查看节区信息(源代码编译后的分区)
readelf -S ./a.out 列出所有节(如 .text, .data, .rodata, .symtab),帮助理解数据和代码如何组织。
查看符号表(类似 nm)
readelf -s ./a.out 或:
readelf --dyn-syms ./libfoo.so 可以查看所有函数、变量的名字、地址、类型、作用域等。
查看动态链接库依赖(类似 ldd)
readelf -d ./a.out 会显示 .dynamic 段内容,例如:
- NEEDED 的库
- SONAME
- INIT_ARRAY
- HASH 表等信息
查看重定位表
readelf -r ./a.out 用于分析 .rel 或 .rela 节(静态链接或动态链接重定位信息)
查看指定节的内容(如字符串表、代码段等)
readelf -x .rodata ./a.out 你可以看到原始的十六进制表示,比如字符串常量、初始化数据等。
addr2line -将地址(十六进制)转换为代码行号
ar -静态库(或存档)工具
as -汇编程序
c++ -C++前端
c++filt -将错误的名称转换为具有原型的功能
cpp -仅预处理器
elfedit -elf标头操作
g++ -具有gnu扩展的C++
gcc -标准二进制文件(给定的选项可以执行与包装程序相同的操作)
gcc-5.4.1 -具有多个GCC安装的系统的全名
gcc-ar -如果有多个“ ar”,则重命名
gcc-nm -如果有多个“ nm”,则重命名
gcc-ranlib -如果有多个“ ranlib”,则重命名
gcov -代码覆盖率
gcov-tool -代码覆盖率
gdb -调试器
gdb-py -更小的调试器
gprof -调用图/分析器
ld -链接器(很可能是金)
ld.bfd -具有更多功能的旧式链接器;对于大型C++项目,速度要慢得多
nm -以二进制形式显示“名称”
objcopy -操作二进制文件(节)
objdump -关于二进制文件的信息
ranlib -生成库索引
readelf -有关ELF二进制文件的信息
size -程序段大小
strings -将所有字符串转储为二进制文件
strip -从二进制文件中删除调试信息
名称“gcc-ar”和“ ar”在物理上是同一件事。但是,路径(Solaris或其他Unix系统)中可能存在另一个“ ar”,并且可以使用“ gcc-ar”名称来获取特定于gcc的“ ar”。所有“ gcc-XXX”都适用于此用例。
addr2line
单个地址转换为源文件位置
addr2line -e ./a.out 0x4005e3 输出示例:
main.c:42 显示函数名(配合 f)
addr2line -e ./a.out -f 0x4005e3 输出示例:
main
main.c:42 还原C++函数名(demangle)
addr2line -e ./my_cpp_app -f -C 0x401234 输出示例:
MyClass::doSomething()
src/myclass.cpp:77 批量地址查询(脚本或多地址)
addr2line -e ./a.out -f -C < crash_addresses.txt 其中 crash_addresses.txt 内容例如:
python-repl
CopyEdit
0x4005e3
0x4006a2
... 使用前提
addr2line 要想准确工作,目标文件必须包含调试符号信息(DWARF):
- 编译时需要加上
g选项(不要加s或strip剥离符号)